I know there are many webservice howto's out there. My motivation to write an other one was, that all the examples i've found just returning simple types like int or String.
This tutorial was inspired by this article on www.mkyong.com.
The Goal:
A webservice, accepting a mathematical expression as input string and returning a complex type, containing the result and the execution time.
Decitions:
Because evaluating expressions strings like "1+2+3*4/3" is kind of tricky in pure java, i decided to use the PythonInterpreter class of Jython to solve the input expresssion.
This example follows an "code first" approach. The WSDL file and XSD file is generated by the JAX-WS framework. After starting the JETTY servlet container ( step 10. ), the WSDL/XSD locations are:
WSDL: http://localhost:9999/calc?wsdl
XSD: http://localhost:9999/calc?xsd=1
Download the complete project: CalcWS.tar.gz
Index:
input message sample:
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:ns0="http://st0ne.at/" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <ns1:Body> <ns0:calculate> <inputString>9*3+14*3+1+7+6+1*2^2*(1+9999999)/0xf</inputString> </ns0:calculate> </ns1:Body> </SOAP-ENV:Envelope>
result message sample:
<?xml version='1.0' encoding='UTF-8'?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:calculateResponse xmlns:ns2="http://st0ne.at/"> <return> <execTime>2</execTime> <result>1333248</result> </return> </ns2:calculateResponse> </S:Body> </S:Envelope>
1. create project structure:
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp \ -DgroupId=at.st0ne \ -DartifactId=CalcWS \ -DinteractiveMode=false
2. customize pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>at.st0ne</groupId> <artifactId>CalcWS</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>CalcWS Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- JAX-WS --> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-rt</artifactId> <version>2.2.7</version> </dependency> <!-- Spring framework --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> <version>2.5.6</version> </dependency> <!-- Library from java.net, integrate Spring with JAX-WS --> <dependency> <groupId>org.jvnet.jax-ws-commons.spring</groupId> <artifactId>jaxws-spring</artifactId> <version>1.8</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </exclusion> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </exclusion> <exclusion> <groupId>com.sun.xml.stream.buffer</groupId> <artifactId>streambuffer</artifactId> </exclusion> <exclusion> <groupId>org.jvnet.staxex</groupId> <artifactId>stax-ex</artifactId> </exclusion> </exclusions> </dependency> <!-- jython for embeding python code --> <dependency> <groupId>jython</groupId> <artifactId>jython</artifactId> <version>2.1</version> </dependency> </dependencies> <build> <finalName>CalcWS</finalName> <plugins> <!-- JETTY plugin for fast testing --> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <version>6.1.5</version> <configuration> <scanIntervalSeconds>2</scanIntervalSeconds> <contextPath>/</contextPath> <connectors> <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector"> <port>9999</port> <maxIdleTime>60000</maxIdleTime> </connector> </connectors> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.1</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>
3. src/main/java/at/st0ne/CalcWS.java.html:
package at.st0ne; import javax.jws.WebMethod; import javax.jws.WebService; import javax.jws.WebParam; import at.st0ne.messages.CalcResult; @WebService public class CalcWS { // Spring Dependency Injection // Calc interface Calc calc; // Spring setter @WebMethod(exclude=true) public void setCalc(Calc calc) { this.calc = calc; } @WebMethod(operationName="calculate") public CalcResult calculate( @WebParam(name = "inputString") String inputString ) { return calc.calculate( inputString ); } }
4. src/main/java/at/st0ne/Calc.java.html:
package at.st0ne; import at.st0ne.messages.CalcResult; public interface Calc { CalcResult calculate(String inputString); }
5. src/main/java/at/st0ne/messages/CalcResult.java:
package at.st0ne.messages; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class CalcResult { private long execTime; private String result; @XmlElement public long getExecTime() { return this.execTime; } public void setExecTime(long execTime) { this.execTime = execTime; } @XmlElement public String getResult() { return this.result; } public void setResult(String result) { this.result = result; } }
6. src/main/java/at/st0ne/CalcImpl.java:
package at.st0ne; import at.st0ne.Calc; import at.st0ne.messages.CalcResult; import org.python.util.PythonInterpreter; public class CalcImpl implements Calc { private static PythonInterpreter py; CalcImpl() { this.py = new PythonInterpreter(); } public CalcResult calculate(String inputString) { long start = System.currentTimeMillis(); CalcResult r = new CalcResult(); r.setResult(py.eval( inputString).toString()); r.setExecTime( System.currentTimeMillis() - start ); return r; } }
7. src/main/webapp/WEB-INF/web.xml:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app id="CalcWS" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>CalcWS</display-name> <servlet> <servlet-name>jaxws-servlet</servlet-name> <servlet-class> com.sun.xml.ws.transport.http.servlet.WSSpringServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>jaxws-servlet</servlet-name> <url-pattern>/calc</url-pattern> </servlet-mapping> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> </web-app>
8. wire things together src/main/webapp/WEB-INF/applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wss="http://jax-ws.dev.java.net/spring/servlet" xmlns:ws="http://jax-ws.dev.java.net/spring/core" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://jax-ws.dev.java.net/spring/core http://jax-ws.dev.java.net/spring/core.xsd http://jax-ws.dev.java.net/spring/servlet http://jax-ws.dev.java.net/spring/servlet.xsd" > <wss:binding url="/calc"> <wss:service> <ws:service bean="#calcWS"/> </wss:service> </wss:binding> <!-- Web service methods --> <bean id="calcWS" class="at.st0ne.CalcWS"> <property name="calc" ref="CalculatorImplementation" /> </bean> <bean id="CalculatorImplementation" class="at.st0ne.CalcImpl" /> </beans>
9. compile and package war file
mvn clean mvn compile mvn package
10. start application in jetty:
mvn run:jetty
11. python/suds test client python/client.py:
#!/usr/bin/python import suds import logging from suds import * from suds.client import Client handler = logging.StreamHandler(sys.stderr) logger = logging.getLogger('suds.transport.http') logger = logging.getLogger('suds.client') logger.setLevel(logging.DEBUG), handler.setLevel(logging.DEBUG) logger.addHandler(handler) client = Client('http://localhost:9999/calc?wsdl', timeout=30) print client print client.service.calculate('9*3+14*3+1+7+6+1*2^2*(1+9999999)/0xf')